﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.TTLV
{
    /// <summary>
    /// TTVL辅助类[编码规则:Tag:4字节;Type:1字节;Length:4字节;Value:bytes]
    /// </summary>
    public partial class TTLVHelper
    {
        static TTLVHelper()
        {
            _ttlvAttributeType = typeof(TTLVAttribute);
            _ttlvIConverterTypeName = typeof(ITTLVConverter).FullName;
            _ienumerableTypeName = typeof(System.Collections.IEnumerable).FullName;
            _memberAcessFlags = BindingFlags.Public | BindingFlags.Instance;
            _codec = new TTLVNodeDefaultCodec();
        }

        private readonly static Type _ttlvAttributeType;
        private readonly static string _ttlvIConverterTypeName;
        private readonly static string _ienumerableTypeName;
        private readonly static BindingFlags _memberAcessFlags;
        private static ITTLVNodeCodec _codec;

        /// <summary>
        /// 设置节点编解码器
        /// </summary>
        /// <param name="codec">节点编解码器</param>
        public static void SetNodeCodec(ITTLVNodeCodec codec)
        {
            if (codec == null)
            {
                throw new ArgumentNullException(nameof(codec));
            }

            _codec = codec;
        }

        #region GetTypeStructTree

        private readonly static Dictionary<Type, TTLVStructNode> _typeStructTreeDic = new Dictionary<Type, TTLVStructNode>();
        private static TTLVStructNode GetTypeStructTree(Type type)
        {
            TTLVStructNode typeStructTree;
            if (_typeStructTreeDic.TryGetValue(type, out typeStructTree))
            {
                return typeStructTree;
            }

            lock (_typeStructTreeDic)
            {
                if (_typeStructTreeDic.TryGetValue(type, out typeStructTree))
                {
                    return typeStructTree;
                }

                typeStructTree = new TTLVStructNode(0, false, TypeCode.Empty, null, null);
                var allNodeDic = new Dictionary<Type, TTLVStructNode>();
                CreateTypeStructTree(allNodeDic, typeStructTree, type);
                _typeStructTreeDic[type] = typeStructTree;
                return typeStructTree;
            }
        }

        private static void CreateTypeStructTree(Dictionary<Type, TTLVStructNode> allNodeDic, TTLVStructNode parentNode, Type type)
        {
            CreateTypePropertyStructTree(allNodeDic, parentNode, type);//属性
            CreateTypeFieldStructTree(allNodeDic, parentNode, type);//字段
        }

        private static void CreateTypeFieldStructTree(Dictionary<Type, TTLVStructNode> allNodeDic, TTLVStructNode parentNode, Type type)
        {
            FieldInfo[] fieldInfoArr = type.GetFields(_memberAcessFlags);
            Type underlyingType;
            TypeCode typeCode;

            foreach (FieldInfo fieldInfo in fieldInfoArr)
            {
                object[] objAtts = fieldInfo.GetCustomAttributes(_ttlvAttributeType, true);
                if (objAtts.Length < 1)
                {
                    //未标记,忽略
                    continue;
                }

                underlyingType = Nullable.GetUnderlyingType(fieldInfo.FieldType);
                if (underlyingType != null)
                {
                    typeCode = Type.GetTypeCode(underlyingType);
                }
                else
                {
                    typeCode = Type.GetTypeCode(fieldInfo.FieldType);
                }


                var ttlvAttribute = (TTLVAttribute)objAtts[0];
                var node = new TTLVStructNode(ttlvAttribute.Tag, underlyingType != null, typeCode, fieldInfo);

                if (typeCode == TypeCode.Object)
                {
                    if (ttlvAttribute.ConverterType != null)
                    {
                        //优先使用自定义转换
                        InitConverterNode(ttlvAttribute, node, fieldInfo.FieldType);
                    }
                    else
                    {
                        InitNoConverterNode(allNodeDic, fieldInfo, ttlvAttribute, node);
                    }
                }
                else
                {
                    node.TTLVType = TTLVType.BasicType;
                }

                if (parentNode.ChildDic.ContainsKey(node.Tag))
                {
                    throw new ArgumentException($"类型\"{fieldInfo.ReflectedType.FullName}\"中字段\"{fieldInfo.Name}\"标记的\"{nameof(TTLVAttribute)}\"特性参数\"{nameof(TTLVAttribute.Tag)}\"值\"{node.Tag}\"重复");
                }

                parentNode.ChildDic.Add(node.Tag, node);
            }
        }

        private static void CreateTypePropertyStructTree(Dictionary<Type, TTLVStructNode> allNodeDic, TTLVStructNode parentNode, Type type)
        {
            PropertyInfo[] propertyInfoArr = type.GetProperties(_memberAcessFlags);
            Type underlyingType;
            TypeCode typeCode;

            foreach (PropertyInfo propertyInfo in propertyInfoArr)
            {
                object[] objAtts = propertyInfo.GetCustomAttributes(_ttlvAttributeType, true);
                if (objAtts.Length < 1)
                {
                    //未标记,忽略
                    continue;
                }

                underlyingType = Nullable.GetUnderlyingType(propertyInfo.PropertyType);
                if (underlyingType != null)
                {
                    typeCode = Type.GetTypeCode(underlyingType);
                }
                else
                {
                    typeCode = Type.GetTypeCode(propertyInfo.PropertyType);
                }

                var ttlvAttribute = (TTLVAttribute)objAtts[0];
                var node = new TTLVStructNode(ttlvAttribute.Tag, underlyingType != null, typeCode, propertyInfo, ttlvAttribute.Index);

                if (typeCode == TypeCode.Object)
                {
                    if (ttlvAttribute.ConverterType != null)
                    {
                        //优先使用自定义转换
                        InitConverterNode(ttlvAttribute, node, propertyInfo.PropertyType);
                    }
                    else
                    {
                        InitNoConverterNode(allNodeDic, propertyInfo, ttlvAttribute, node);
                    }
                }
                else
                {
                    node.TTLVType = TTLVType.BasicType;
                }

                if (parentNode.ChildDic.ContainsKey(node.Tag))
                {
                    throw new ArgumentException($"类型\"{propertyInfo.ReflectedType.FullName}\"中属性\"{propertyInfo.Name}\"标记的\"{nameof(TTLVAttribute)}\"特性参数\"{nameof(TTLVAttribute.Tag)}\"值\"{node.Tag}\"重复");
                }

                parentNode.ChildDic.Add(node.Tag, node);
            }
        }

        private static void InitNoConverterNode(Dictionary<Type, TTLVStructNode> allNodeDic, PropertyInfo propertyInfo, TTLVAttribute ttlvAttribute, TTLVStructNode node)
        {
            switch (ttlvAttribute.PropertyType)
            {
                case TTLVPropertyType.Collection:
                    Type eleType;
                    TTLVType ttlvType;
                    TTLVCommon.GetCollectionElementType(propertyInfo.PropertyType, out eleType, out ttlvType);
                    node.TTLVType = ttlvType;
                    node.ElementType = eleType;
                    CreateTypeStructTree(allNodeDic, node, eleType);
                    break;
                case TTLVPropertyType.SingleObject:
                    if (allNodeDic.ContainsKey(propertyInfo.PropertyType))
                    {
                        //解决递归类型,即类型同嵌套自身类型
                        node.RefNode = allNodeDic[propertyInfo.PropertyType];
                    }
                    else
                    {
                        allNodeDic.Add(propertyInfo.PropertyType, node);
                        node.ElementType = propertyInfo.PropertyType;
                        CreateTypeStructTree(allNodeDic, node, propertyInfo.PropertyType);
                    }

                    node.TTLVType = TTLVType.Object;
                    break;
                default:
                    throw new NotImplementedException($"未实现的TTLVPropertyType[{ttlvAttribute.PropertyType.ToString()}]");
            }
        }

        private static void InitNoConverterNode(Dictionary<Type, TTLVStructNode> allNodeDic, FieldInfo fieldInfo, TTLVAttribute ttlvAttribute, TTLVStructNode node)
        {
            switch (ttlvAttribute.PropertyType)
            {
                case TTLVPropertyType.Collection:
                    Type eleType;
                    TTLVType ttlvType;
                    TTLVCommon.GetCollectionElementType(fieldInfo.FieldType, out eleType, out ttlvType);
                    node.TTLVType = ttlvType;
                    node.ElementType = eleType;
                    CreateTypeStructTree(allNodeDic, node, eleType);
                    break;
                case TTLVPropertyType.SingleObject:
                    if (allNodeDic.ContainsKey(fieldInfo.FieldType))
                    {
                        //解决递归类型,即类型同嵌套自身类型
                        node.RefNode = allNodeDic[fieldInfo.FieldType];
                    }
                    else
                    {
                        allNodeDic.Add(fieldInfo.FieldType, node);
                        node.ElementType = fieldInfo.FieldType;
                        CreateTypeStructTree(allNodeDic, node, fieldInfo.FieldType);
                    }

                    node.TTLVType = TTLVType.Object;
                    break;
                default:
                    throw new NotImplementedException($"未实现的TTLVPropertyType[{ttlvAttribute.PropertyType.ToString()}]");
            }
        }

        private static void InitConverterNode(TTLVAttribute ttlvAttribute, TTLVStructNode node, Type eleType)
        {
            if (ttlvAttribute.ConverterType.GetInterface(_ttlvIConverterTypeName) == null)
            {
                throw new ArgumentException(string.Format("转换器类型[{0}]未实现[{1}]接口", ttlvAttribute.ConverterType.FullName, _ttlvIConverterTypeName));
            }

            node.Converter = CreateTTLVConverter(ttlvAttribute);
            node.ConverterPara = ttlvAttribute.ConverterPara;
            node.TTLVType = TTLVType.Convert;
            node.ElementType = eleType;
        }

        private static Hashtable _htConverter = new Hashtable();
        private static ITTLVConverter CreateTTLVConverter(TTLVAttribute ttlvAttribute)
        {
            ITTLVConverter converter;
            var key = ttlvAttribute.ConverterType;
            if (_htConverter.ContainsKey(key))
            {
                converter = (ITTLVConverter)_htConverter[key];
            }
            else
            {
                converter = (ITTLVConverter)Activator.CreateInstance(ttlvAttribute.ConverterType);
                _htConverter[key] = converter;
            }

            return converter;
        }

        #endregion

        private static Dictionary<int, TTLVStructNode> GetTTLVStructNodeChilds(TTLVStructNode node)
        {
            Dictionary<int, TTLVStructNode> childs;
            if (node.RefNode != null)
            {
                childs = node.RefNode.ChildDic;
            }
            else
            {
                childs = node.ChildDic;
            }

            return childs;
        }

    }
}
